package littlecyberwar.tool;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;

import littlecyberwar.ui.*;
import littlecyberwar.model.*;

import littlecyberwar.util.*;

/**
 * This is a superclass for all angular movement (center, left right)
 * mouse handler
 * In the would of angular movement, the pixMoved member is sign significant.
 * A positive number signifies clockwise, and negative = counter. The abs value
 * is calced when the value is displayed onto the status bar.
 */
public class MouseRotateHandler extends MultiSelectMouseHandler {
  
	public final double fortyFiveDegree = Math.PI/4.0;
	double startAngle, origStartAngle;
  
  /**
   * This is the width of the largest element currently selected.
   * If there are more than one element, then we need to find the largest one here
   * 
   * Now if the shift key is selected, then we have a "group" rotate, in which case
   * this field== the width of the "selected rectangle" which is a union of all elements 
   */
  double fullWidth;
  boolean clockwise;
  
  /**
   * This boolean is set to true iff all the elements selected are facing the same direction
   * Else only rotate around the center is allowed 
   */
  boolean allowWheel;
  
  /**
   * If true, then it's rotating the combined "shape", else each individual shape rotates
   *
   */
  boolean isCombinedShape;

  Point2D pivotP;
  double startDragX, startDragY, pivotX, pivotY;
	boolean groupRotate = false;	// set to true to rotate around the entire group
	
	
	
	
  public MouseRotateHandler() {
    super();
  }

  public void startAction( MouseEvent e ) {
    super.startAction(e);
  	Point scaledP = ScalingDelegate.getMouseEventLocation(e.getPoint());

    // fullWidth = currEl.getWidth();
    
    // in case of shift down, go to a different route
	int onmask = InputEvent.SHIFT_DOWN_MASK;		
	if ((e.getModifiersEx() & onmask) == onmask) {
		setupGroupRotateParam(e);
		setGroupPivotParam();
	} else {
		setupRotateParam(e);
		setPivotParam();
	}
	
    clockwise=false;
	allowWheel=false;
	isCombinedShape = false;
	
	startDragX = scaledP.getX();
	startDragY = scaledP.getY();
    
	startAngle = Math.atan2((startDragY-pivotY),(startDragX-pivotX));
	origStartAngle = startAngle;	
	
	DirtyAreaManager.checkAndRepaint(comp);
  }

  protected UnitCommand getUnitCommand(Integer elementId, Integer handlerId, AffineTransform trans, double pixMoved) {
  	UnitCommand cmd = new UnitRotateCommand(elementId, handlerId, trans, pixMoved, fullWidth/2);
  	return ( cmd );
  }
  

  // to be overridden by subclass
  void setPivotParam() {	
  }
  
  void setGroupPivotParam() {	
  }
  
  
  /**
   * This is the width of the largest element currently selected.
   * If there are more than one element, then we need to find the largest one here
   * 
   * Now if the shift key is selected, then we have a "group" rotate, in which case
   * this field== the width of the "selected rectangle" which is a union of all elements 
   */
  void setupRotateParam( MouseEvent e ) {
  	double val = 0;
  	// check event to see if shift key is held
	// shift is not down, find currentDiameter
	fullWidth = getCurrentDiameter(isCombinedShape);
	return;  	
  }

  void setupGroupRotateParam( MouseEvent e ) {
	isCombinedShape = true;
	fullWidth = getCurrentDiameter(isCombinedShape);
	return;
  }
  	
	double getCurrentDiameter(boolean isCombinedShape){
		double largestWidth= 0;
		if ( isCombinedShape == false ){
			// need to find the largest element
			for ( int i=0; i < selectedElements.size(); i++ ) {
				Element el = ((SelectedElementContainer)selectedElements.get(i)).element;
				double currWidth =el.getWidth(); 
				if ( currWidth > largestWidth ){
					largestWidth = currWidth;
				}
			}
			
		}else{
			// TBD
		}
		return(largestWidth);
	}
	
  /**
   * Override due to different way of calculating distance moved and to print out
   */
  /**
   * This routine will calculate the angular movementment amount in pixels
   * based on starting angle, ending angle, last direction (clockwise or
   * counter-clockwise, also on the radius (full unit width for corner rotate,
   * or 1/2 unit width for center rotate.
   * The basic algorithm is that if the new angle is > old angle then we have a
   * clockwise motion, else counter clockwise.
   * The only gotcha for the algorithm above is when mouse position crossed from
   * +PI to/from -PI, in which case the special boundry condition must be tested.
   */
  protected double getAngularDistanceMoved(
    double currAmount,
    double startAngle,
    double newAngle,
    double radius
    ) {

    double theta = newAngle - startAngle;

    /**
     * Figure out how much distance moved.
     * Need to track special attention if there's a sign change.
     * If it went from pi to -pi, then there was a wrap, and we
     * need to break the cal into two parts
     */
    if ( (startAngle > Math.PI/2) && (newAngle < -Math.PI/2)) {
     /**
      * there's clockwise crossover
      * divide up the cross over to two halves and calculate the
      * pixMoved by adding them together
      * this is CLOCKWISE
      */
      double amount = (Math.abs(Math.PI - startAngle)  + Math.abs( Math.PI + newAngle))*radius;
      //System.out.println("amount=" + amount);
      if ( clockwise == true ) {
        //System.out.println("1" );
        currAmount +=  amount;
      } else {
        // there's a change of direction from counter ==> clock
        //System.out.println("2" );
        currAmount += amount;
        clockwise = true;
        //System.out.println("2.clockwise ==> true");
      }
    } else if ((startAngle < -Math.PI/2) && (newAngle > Math.PI/2)) {
      double amount = (Math.abs(Math.PI - newAngle)  + Math.abs(startAngle + Math.PI))*radius;
      /**
       * this is counter clockwise
       */
       if ( clockwise == true ) {
        System.out.println("3" );
         currAmount -=  amount;
          clockwise = false;
          //System.out.println("3. clockwise ==> false");
       } else {
        // remains counter cw
        //System.out.println("4" );
        currAmount -= amount;
       }

    } else if ( newAngle != startAngle) {
      // no cross over, normal operation
      // also throw out degenerate cases where two angles are equal
      double amount = Math.abs(theta * radius);
      if ( newAngle < startAngle ) {
        // this is counter cw
        if ( clockwise == true ) {
          currAmount -=  amount;
          clockwise = false;
          //System.out.println("start=" + startAngle + ", new=" + newAngle);
          //System.out.println("x clockwise ==> false");

        } else {
          currAmount -= amount;
        }
      } else {
        // new move is clockwise
        if ( clockwise == true ) {
          currAmount +=  amount;
        } else {
          currAmount += amount;
          clockwise = true;
          //System.out.println("start=" + startAngle + ", new=" + newAngle);
          //System.out.println("y clockwise ==> true");
        }
      }
    }
    return( currAmount );
  }
  
  /**
   * <p>
   * Need to figure out which quadrant the current angle vs the last angle
   * due to the discontinus nature of polar coordinate (a discontinuity from
   * +Pi ==> -Pi
   * Need to find when such discontinuity is crossed, break the angle traversed
   * in two, add up the amount traveled to get the sum.
   * </p>
   */
  protected void dragSelected( Point newP ) {
  	// Point scaledP = ScalingDelegate.getMouseEventLocation(newP);
	double x = newP.getX();
	double y = newP.getY();
	double newAngle = Math.atan2((y-pivotY),(x-pivotX));

	double theta = newAngle - startAngle;

	// if the isCntrlSet == true, then we wish to rotate to 45 degree increments
	//
	if ( isCntrlSet ) {
		theta = adjustAngleToFortyFiveDegreeIncrement( theta );
		newAngle = theta + startAngle;
	}
	
	Shape originalRec;
	AffineTransform currTrans;

	// loop through each element and then rotate that amount theta
	for ( int i=0; i < selectedElements.size(); i++ ){
		SelectedElementContainer cont = ((SelectedElementContainer)selectedElements.get(i));
		//Debug.out("cont.halfWidth= " + cont.halfWidth + ", cont.halfHeight= " + cont.halfHeight);
		cont.currTrans.rotate(theta, cont.halfWidth, cont.halfHeight);
		originalRec = cont.element.getRectangle();
		cont.selectedRectangle = cont.currTrans.createTransformedShape(originalRec);
		DirtyAreaManager.addDirtyArea(cont.selectedRectangle);		
	}

	// System.out.println("currTrans changed to: " + currTrans);
	startDragX = x;
	startDragY = y;
	pixMoved = getAngularDistanceMoved(pixMoved, startAngle, newAngle, fullWidth /2);
	//Debug.out("Angle Theta=" + Math.toDegrees(theta));
	startAngle = newAngle;
	return;
  }
  
  protected double adjustAngleToFortyFiveDegreeIncrement( double oldAngle ){
  	double newAngle = oldAngle;
  	
  	return ( newAngle );
  }

  public void handlerSelected(){
  	super.handlerSelected();
	startAngle = 0; 
	origStartAngle=0;
	fullWidth = 0;
	clockwise = false;
	allowWheel = false;
	isCombinedShape = false;
	pivotP = null;
	startDragX = 0; startDragY=0; pivotX=0; pivotY=0;
	groupRotate = false;
	return;
  }
  
  protected void printToStatus( String val ){
  	double pixInUnit = (pixMoved/mapScale);
  	double radius = (fullWidth/2);
  	comp.printToStatus( "Unit tentatively moved: " + df.format(pixInUnit) + " " + mapDistanceUnit + ", (" + MouseHandler.df.format(Math.toDegrees(pixMoved/ radius)) + " Degrees)"  );
  }


}